#include "Avalon/Codecs/CodedReader.hpp"
#include <boost/bind.hpp>
#include "Avalon/Codecs/Decoder.hpp"
#include "Avalon/IO/IOException.hpp"

using namespace Avalon;
using namespace Avalon::Codecs;
using namespace Avalon::IO;
using namespace Avalon::Threading;
using namespace boost;

CodedReader::CodedReader(Carry<Reader> source, Decoder* decoder)
    : m_source(source),
      m_decoder(decoder) {}

CodedReader::~CodedReader() {}

bool CodedReader::IsAtEndOfDataSource() {
  return m_source->IsAtEndOfDataSource();
}

Async<int>::Ptr CodedReader::Read(Buffer* destination) {
  lock_guard<recursive_mutex> lock(m_mutex);
  assert(m_sourceRead == NULL);
  m_initialDestination.Reset();
  m_sourceRead = m_source->Read(&m_initialDestination);
  AsyncOwner<int>::Ptr decodedRead = AsyncOwner<int>::Create();
  m_sourceRead->ConnectFinishedSignal(bind(&CodedReader::OnBufferRead, this,
    destination, decodedRead, m_sourceRead));
  return decodedRead;
}

Async<int>::Ptr CodedReader::Read(void* destination, int size) {
  lock_guard<recursive_mutex> lock(m_mutex);
  assert(m_sourceRead == NULL);
  m_initialDestination.Reset();
  m_sourceRead = m_source->Read(&m_initialDestination, size);
  AsyncOwner<int>::Ptr decodedRead = AsyncOwner<int>::Create();
  m_sourceRead->ConnectFinishedSignal(bind(&CodedReader::OnDataRead, this,
    destination, size, decodedRead, m_sourceRead));
  return decodedRead;
}

Async<int>::Ptr CodedReader::Read(Buffer* destination, int size) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  assert(m_sourceRead == NULL);
  m_initialDestination.Reset();
  m_sourceRead = m_source->Read(&m_initialDestination, size);
  AsyncOwner<int>::Ptr decodedRead = AsyncOwner<int>::Create();
  m_sourceRead->ConnectFinishedSignal(bind(&CodedReader::OnBufferRead, this,
    destination, decodedRead, m_sourceRead));
  return decodedRead;
}

void CodedReader::OnBufferRead(Buffer* destination,
    const AsyncOwner<int>::Ptr& decodedRead,
    const Async<int>::Ptr& sourceRead) {
  lock_guard<recursive_mutex> lock(m_mutex);
  m_sourceRead.reset();
  if(sourceRead->GetState() == BaseAsync::EXCEPTION) {
    decodedRead->CopyException(sourceRead);
    return;
  }
  try {
    int decodedSize;
    m_decoder->Decode(m_initialDestination, destination, &decodedSize);
    decodedRead->SetResult(decodedSize);
  } catch(boost::exception&) {
    decodedRead->SetException(current_exception());
  } catch(...) {
    decodedRead->SetException(IOException("Unknown error."));
  }
}

void CodedReader::OnDataRead(void* destination, int size,
    const AsyncOwner<int>::Ptr& decodedRead,
    const Async<int>::Ptr& sourceRead) {
  lock_guard<recursive_mutex> lock(m_mutex);
  m_sourceRead.reset();
  if(sourceRead->GetState() == BaseAsync::EXCEPTION) {
    decodedRead->CopyException(sourceRead);
    return;
  }
  try {
    int decodedSize;
    m_decoder->Decode(m_initialDestination, destination, size, &decodedSize);
    decodedRead->SetResult(decodedSize);
  } catch(boost::exception&) {
    decodedRead->SetException(current_exception());
  } catch(...) {
    decodedRead->SetException(IOException("Unknown error."));
  }
}
